type HeartCheck = {
  timeout: number
  timeoutObj: AnyType | null
  reset: () => HeartCheck
  start: (cb: () => void) => void
}

const heartCheck: HeartCheck = {
  timeout: 60000, //60秒
  timeoutObj: null,
  reset: function () {
    if (this.timeoutObj) clearInterval(this.timeoutObj)
    return this
  },
  start: function (cb) {
    this.timeoutObj = setInterval(function () {
      //这里发送一个心跳，后端收到后，返回一个心跳消息，
      // onmessage拿到返回的心跳就说明连接正常
      cb()
    }, this.timeout)
  },
}

type CoolWebsocketProps = {
  url?: string
  onOpen?: () => void
  onError?: (e: Event) => void
  onClose?: (e: CloseEvent) => void
  onMessage?: (e: MessageEvent) => void
}

interface CoolWebsocketImpl {
  core: WebSocket
  reconnect: () => void
  url: string | ((url: string) => void)
  onOpen: (() => void) | undefined
  onError: ((e: Event) => void) | undefined
  onClose: ((e: CloseEvent) => void) | undefined
  onMessage: ((e: MessageEvent) => void) | undefined
}

export class CoolWebsocket implements CoolWebsocketImpl {
  private _baseUrl = import.meta.env.VITE_WS_URL
  private _url = ''
  private _lockReconnect = false
  private _errorTryTimes = 0
  core
  onOpen
  onError
  onClose
  onMessage

  constructor(props?: CoolWebsocketProps | string) {
    // 根据props更新配置
    // console.log(process.env.NODE_ENV)
    // const prefix = process.env.NODE_ENV === 'production' ? 'wss' : 'ws'
    // this._url = 'ws://' + location.host
    // this._url = 'ws://127.0.0.1:3000'
    this._url = `ws://${this._baseUrl}:3000`
    if (typeof props === 'string') {
      this._url += props
    } else {
      if (props?.onOpen && typeof props.onOpen === 'function')
        this.onOpen = props.onOpen
      if (props?.onError) this.onError = props.onError
      if (props?.onClose) this.onClose = props.onClose
      if (props?.onMessage) this.onMessage = props.onMessage
      this._url += props?.url ?? ''
      // if (props?.userIdTrail !== false) {
      //   if (this._url.endsWith('/')) this._url += store.getters.getUserId
      //   else this._url += '/' + store?.getters?.getUserId
      // }
      // this._url = `ws://212.129.246.194:8182/ws/${props?.url}`
    }
    this.core = new WebSocket(this._url)
    this._makeWebSocketOptions()
  }

  reconnect() {
    try {
      if (this._lockReconnect) return
      this._lockReconnect = true
      this.core.close(1000)
      this.core = new WebSocket(this._url)
      this._makeWebSocketOptions()
      this._lockReconnect = false
    } catch (e) {
      console.log('ws try catch', e)
    }
  }

  on(
    type: 'open' | 'close' | 'error' | 'message',
    callback: (e?: any) => void,
  ) {
    switch (type) {
      case 'open':
        this.onOpen = callback
        break
      case 'close':
        this.onClose = callback
        break
      case 'error':
        this.onError = callback
        break
      case 'message':
        this.onMessage = callback
        break
      default:
        break
    }
  }

  dispose() {
    this.core.close(1000)
  }

  private _makeWebSocketOptions() {
    this.core.onopen = () => {
      this._errorTryTimes = 0
      heartCheck.reset().start(() => this.core.send(''))
      this.onOpen?.()
    }

    this.core.onerror = e => {
      console.log('ws error', e)
      this._errorTryTimes++
      if (this._errorTryTimes <= 3) this.reconnect()
      this.onError?.(e)
    }

    this.core.onclose = e => {
      console.log('ws close', e)
      if (e.code !== 1000) {
        this._errorTryTimes++
      }

      setTimeout(() => {
        this.core.onopen = null
        this.core.onerror = null
        this.core.onclose = null
        this.core.onmessage = null
      }, 1)
      heartCheck.reset()

      if (this._errorTryTimes <= 10) this.reconnect()

      this.onClose?.(e)
      /* console.log(
        'websocket 断开: ' + e.code + ' ' + e.reason + ' ' + e.wasClean,
      ) */
    }

    this.core.onmessage = e => {
      //event 为服务端传输的消息，在这里可以处理
      // console.log('websocket 连接' + e)
      this.onMessage?.(e)
    }
  }

  get url(): string {
    return this._url
  }

  set url(value: string) {
    this._url = value
    this.reconnect()
  }
}
